package com.ssm.changeunit.command;

import com.ssm.changeunit.*;
import com.ssm.changeunit.persistance.PersistanceException;
import com.ssm.changeunit.diff.*;
import com.ssm.changeunit.persistance.ChangeUnitDAO;
import com.ssm.changeunit.persistance.DatabaseManager;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;

/**
 * Diff 명령을 처리하는 Worker
 * 
 * 두 버전을 비교해서 그 차이점을 출력한다.
 * 
 * @author 시리우스
 */
public class DiffCommandWorker extends CommandWorker {
    private Logger log = Logger.getLogger(DiffCommandWorker.class);

    public DiffCommandWorker () {
    }

    public void work (CommandWorkerOptions commandWorkerOptions) throws CommandWorkerException {
        if( commandWorkerOptions == null )
            throw new CommandWorkerException("Option이 null입니다.");
        
        if( !commandWorkerOptions.hasCommandWorkerOption("dest") )
            throw new CommandWorkerException("Dest Option이 존재하지 않습니다.");
        
        try {
            if(!DatabaseManager.getInstance().isExists())
                throw new CommandWorkerException("데이터베이스가 존재하지 않습니다.");
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
        
        // Origin option을 가져온다.
        CommandWorkerOption originCommandWorkerOption = commandWorkerOptions.getCommandWorkerOption("origin");
        
        ChangeUnit originChangeUnit = null;
        ChangeUnit destChangeUnit = null;
        
        if(originCommandWorkerOption != null) {
            try {
                // Origin option이 존재하는 경우
                String originChangeUnitId = originCommandWorkerOption.getValues()[0];

                ChangeUnitDAO changeUnitDAO = new ChangeUnitDAO();
                
                if( changeUnitDAO.find(originChangeUnitId) == null || originChangeUnitId.equals("0") )
                    throw new CommandWorkerException("Origin ChangeUnit을 찾지 못했습니다. "+ originChangeUnitId);
                else
                    originChangeUnit = new ChangeUnitFactory().createProduct(originChangeUnitId);
            }
            catch (CreateException ex) {
                throw new CommandWorkerException("ChangeUnit을 가져올수 없습니다.", ex);
            }
            catch(PersistanceException ex) {
                throw new CommandWorkerException("ChangeUnit을 가져올수 없습니다.", ex);
            }
        }
        else {
            try {
                // Origin option이 존재하지 않는 경우
                // 가장 최근의 ChangeUnit을 Origin으로 설정한다.
                Branch currentBranch = new BranchFactory().createProduct(Environment.getCurrentBranch());
                
                originChangeUnit = currentBranch.getCurrentChangeUnit();
                
                if(originChangeUnit.getChangeUnitId().equals("0"))
                    throw new CommandWorkerException("Origin ChangeUnit을 찾을 수 없습니다. ");
            }
            catch (IOException ex) {
                throw new CommandWorkerException("Branch를 가져올수 없습니다.", ex);
            }            
            catch (CreateException ex) {
                throw new CommandWorkerException("Branch를 가져올수 없습니다.", ex);
            }
            catch(PersistanceException ex) {
                throw new CommandWorkerException("ChangeUnit을 가져올수 없습니다.", ex);
            }
        }
        
        CommandWorkerOption destCommandWorkerOption = commandWorkerOptions.getCommandWorkerOption("dest");
        
         try {
            String destChangeUnitId = destCommandWorkerOption.getValues()[0];

            ChangeUnitDAO changeUnitDAO = new ChangeUnitDAO();
                
            if( changeUnitDAO.find(destChangeUnitId) == null || destChangeUnitId.equals("0"))
                throw new CommandWorkerException("Dest ChangeUnit을 찾지 못했습니다. " + destChangeUnitId);
            else
                destChangeUnit = new ChangeUnitFactory().createProduct(destChangeUnitId);
        }
        catch (CreateException ex) {
            throw new CommandWorkerException("ChangeUnit을 가져올수 없습니다.", ex);
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException("ChangeUnit을 가져올수 없습니다.", ex);
        }
        
        log.debug("Origin ChangeUnit ID: " + originChangeUnit.getChangeUnitId());
        log.debug("Dest ChangeUnit ID: " + destChangeUnit.getChangeUnitId());
        
        try {
            // Origin의 ChangeUnitItem을 가져온다.
            List<ChangeUnitItem> originChangeUnitItems = originChangeUnit.getChangeUnitItems();
            
            // Dest의 ChangeUnitItem을 가져온다.
            List<ChangeUnitItem> destChangeUnitItems = destChangeUnit.getChangeUnitItems();
            
            for(ChangeUnitItem originChangeUnitItem : originChangeUnitItems) {
                String originFilePath = originChangeUnitItem.getFilePath();
                
                ChangeUnitItem destChangeUnitItem = null;
                
                for(ChangeUnitItem tmpDestChangeUnitItem : destChangeUnitItems) {
                    String destFilePath = tmpDestChangeUnitItem.getFilePath();
                    
                    // 같다면
                    if( originFilePath.equals(destFilePath) ) {
                        destChangeUnitItem = tmpDestChangeUnitItem;
                        
                        break;
                    }
                }
                
                // Origin엔 있는데 Dest에 없는 경우
                if( destChangeUnitItem == null ) {
                    log.info("Origin: " + originFilePath + " 존재");
                    log.info("Dest: " + originFilePath + " 존재하지 않음");
                }
                // Origin에 있고, Dest에도 있는 경우
                else {
                    ChangeLog originChangeLog = originChangeUnitItem.getRefChangeLog();
                    ChangeLog destChangeLog = destChangeUnitItem.getRefChangeLog();
                    
                    // 비교
                    List diffs  = (
                            new Diff(
                                new String(originChangeLog.getContent()).split("\n"),
                                new String(destChangeLog.getContent()).split("\n"))
                            ).diff();

                    Iterator it = diffs.iterator();

                    while (it.hasNext()) {
                        Difference diff     = (Difference)it.next();
                        int        delStart = diff.getDeletedStart();
                        int        delEnd   = diff.getDeletedEnd();
                        int        addStart = diff.getAddedStart();
                        int        addEnd   = diff.getAddedEnd();
                        String     type     = delEnd != Difference.NONE && addEnd != Difference.NONE ? "change" : (delEnd == Difference.NONE ? "add" : "delete");

                        // 추가된 경우
                        if(type.equals("add")) {
                            log.info("Dest에 추가되었습니다.");
                            log.info("addStart: " + addStart + " addEnd: " + addEnd);
                        }
                        // 삭제된 경우
                        else if(type.equals("delete")) {
                            log.info("Dest에 삭제되었습니다.");
                            log.info("delStart: " + delStart + " delEnd: " + delEnd);
                        }
                        // 변경된 경우
                        else if(type.equals("change")) {
                            log.debug("Change. addStart: " + addStart + " addEnd: " + addEnd + " delStart: " + delStart + " delEnd: " + delEnd);
                        }
                    }
                }
            }
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
    }
}